#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
//第18讲：数据在内存中的存储+第19讲：⾃定义类型：结构体
//unsigned char i = 0;
////unsgined char范围是0~255 
//int main()
//{
//	for (i = 0; i <= 255; i++)//255+1 --> 0
//	{
//		printf("hello world\n");
//	}
//	return 0;
//}

//int main()
//{
//	unsigned int i;
//	for (i = 9; i >= 0; i--)
//	{
//		printf("%u\n", i);
//		Sleep(500);
//	}
//	return 0;
//}

//2.3.6 练习6
//X86环境 ⼩端字节序
//int main()
//{
//	int a[4] = { 1, 2, 3, 4 };
//	int* ptr1 = (int*)(&a + 1);
//	int* ptr2 = (int*)((int)a + 1);//地址+1向后跳过一个字节
//	printf("%x,%x", ptr1[-1], *ptr2);
//	return 0;
//}


//3. 浮点数(小数)在内存中的存储
//常⻅的浮点数：3.14159、1E10等，浮点数家族包括： float、double、long double 类型。
//浮点数表⽰的范围： float.h 中定义
//3.1 练习	
//int main()
//{
//	int n = 9;
//	float* pFloat = (float*)&n;
//	printf("n的值为：%d\n", n);//9
//	printf("*pFloat的值为：%f\n", *pFloat);//按道理来说应该是9.000000
//	*pFloat = 9.0;
//	printf("num的值为：%d\n", n);//按道理来说应该是9
//	printf("*pFloat的值为：%f\n", *pFloat);//9.000000
//	//中间两个错了
//	//分析前两个：9是以整数的形式存放在内存中的，但当以浮点型的形式打印出来的时候结果不一样
//	//可以初步得出一个结论：整型和浮点型数据在内存中的存储是不一样的（当然也有可能一样，可能是对整型和浮点型数据的解读方式不一样）
//	//分析后两个，把9.0以浮点型存放在内存中，以浮点型打印出来的结果正常，但当以整型打印出来就有问题了
//	//也能看出来：整型和浮点型在内存中的存储不一样（也有可能是解读方式不同）
//	return 0;
//}

//3.2 浮点数的存储
//根据国际标准IEEE（电⽓和电⼦⼯程协会） 754，任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式：
//V = (−1)^S * M * 2^E
//(−1)^S 表⽰符号位，当S=0，V为正数；当S=1，V为负数
//M 表⽰有效数字，M是⼤于等于1，⼩于2的	
//2^E 表⽰指数位
//举例：5.5
//二进制转换为:101.1
//表示成 V = (−1)^S *   M   * 2^E
//为:  5.5 = (-1)^0 * 1.011 * 2^2
//其中S=0  M=1.011  E=2
//对于32位的浮点数，最⾼的1位存储符号位S，接着的8位存储指数E，剩下的23位存储有效数字M
//对于64位的浮点数，最⾼的1位存储符号位S，接着的11位存储指数E，剩下的52位存储有效数字M

//3.2.1 浮点数存的过程
// IEEE 754 对有效数字M和指数E，还有⼀些特别规定。
//IEEE 754 规定，在计算机内部保存M时，默认这个数的第⼀位总是1，因此可以被舍去，只保存后⾯的
//xxxxxx部分。⽐如保存1.01的时候，只保存01，等到读取的时候，再把第⼀位的1加上去。这样做的⽬
//的，是节省1位有效数字。以32位浮点数为例，留给M只有23位，将第⼀位的1舍去以后，等于可以保
//存24位有效数字。
//⾄于指数E，情况就⽐较复杂
//⾸先，E为⼀个⽆符号整数（unsigned int）
//这意味着，如果E为8位，它的取值范围为0~255；如果E为11位，它的取值范围为0~2047。但是，我
//们知道，科学计数法中的E是可以出现负数的（比如0.5=1.0*2^-1），所以IEEE 754规定，存⼊内存时E的真实值必须再加上
//⼀个中间数，对于8位的E，这个中间数是127；对于11位的E，这个中间数是1023。⽐如，2 ^ 10的E是
//10，所以保存成32位浮点数时，必须保存成10 + 127 = 137，即10001001。
//int main()
//{
//	float f = 5.5f;
//	//101.1
//	//(-1)^0 * 1.011 * 2^2
//	//S=0 第一位存放
//	//M=1.011  23位(注意M只存小数点后面的位)
//	//E=2  8位
//	// E还要再+127=129
//	//在内存中就是这样存放:
//	// 0 10000001 01100000000000000000000
//	//即: 01000000 10110000 00000000 00000000
//	//十六:  40 B0 00 00 (这就是5.5在内存中的存储样子)
//	return 0;
//}
//注意！
//浮点数在内存中有可能无法精确保存


//3.2.2 浮点数取的过程
//指数E从内存中取出还可以再分成三种情况：
//E不全为0或不全为1
//这时，浮点数就采⽤下⾯的规则表⽰，即指数E的计算值减去127（或1023），得到真实值，再将有效
//数字M前加上第⼀位的1。
//⽐如：0.5 的⼆进制形式为0.1，由于规定正数部分必须为1，即将⼩数点右移1位，则为1.0 * 2 ^ (-1)，其
//阶码为 - 1 + 127(中间值) = 126，表⽰为01111110，⽽尾数1.0去掉整数部分为0，补⻬0到23位
//00000000000000000000000，则其⼆进制表⽰形式为:
//0 01111110 00000000000000000000000

//E全为0
//真实的E=1-127=-126（就是这么定义的）
//有效数字M取出后不会再加上第一位的1

//E全为1
//这时，如果有效数字M全为0，表⽰±⽆穷⼤（正负取决于符号位s）；
//0 11111111 00010000000000000000000

//3.3 题⽬解析
//下⾯，让我们回到⼀开始的练习
//int main()
//{
//	int n = 9;
//	float* pFloat = (float*)&n;
//	printf("*pFloat的值为：%f\n", *pFloat);
//	//9的补码:00000000 00000000 00000000 00001001
//	//站在pFloat的视角，他会认为自己指向的是一个float类型的数值
//	//pFloat视角:0 00000000 00000000000000000001001
//	//E全为0
//	//真实的E=1-127=-126（就是这么定义的,补药纠结为什莫不是0-127惹！）
//	//有效数字M取出后不会再加上第一位的1
//	//得到: (-1)^0 * 0.00000000000000000001001 * 2^-126 (二进制) -->  无限接近于0
//
//	*pFloat = 9.0;
//	printf("num的值为：%d\n", n);
//	//以浮点数的视角存储9.0
//	//先要把9.0转换为二进制:
//	// 1001.0 
//	//(-1)^0 * 1.001 * 2^3
//	//S=0     M=1.001  E=3+127=130
//	//现在把这个浮点数以浮点数的形式存储在内存中:
//	// 0 10000010 00100000000000000000000
//	//%d打印有符号整数。最高位符号位为0，判断为正数，正数的原码反码补码相同，直接打印出这个二进制对应的十进制
//	return 0;
//}



//第19讲：⾃定义类型：结构体
//回顾:
//1.1.2 结构体变量的创建和初始化
//struct stu
//{
//	char name[20];
//	char id[20];
//	int height;
//	float score;
//}s1;
////s1是全局变量
//int main()
//{
//	//初始化:
//	struct stu s2 = { "zhangsan","114514",180,98.5 };//顺序初始化
//	struct stu s3 = { .height=167,.name="lisi",.score=67.3,.id="1919810"};//用结构体成员访问操作符进行初始化
//
//	//打印:
//	printf("%s %s %d %f", s2.name, s2.id, s2.height, s2.score);
//	return 0;
//}


//1.2 结构的特殊声明
//在声明结构的时候，可以不完全的声明，比如:
//匿名结构体类型
//struct
//{
//	int a;
//	char b;
//	float c;
//}x = { 1,'x',3.14 };//匿名结构体只能在struct的末尾进行初始化，在全局和局部都不能初始化
//int main()
//{
//	printf("%d %c %f", x.a, x.b, x.c);
//	return 0;
//}
//匿名的结构体类型，如果没有对结构体类型重命名的话，基本上只能使⽤⼀次。


//1.3 结构的⾃引⽤
//在结构中包含⼀个类型为该结构本⾝的成员是否可以呢？
//⽐如，定义⼀个链表的节点：
//struct Node
//{
//	int data;
//	struct Node next;
//};
//这样做是不正确的，因为⼀个结构体中再包含⼀个同类型的结构体变量，这样结构体变量的⼤⼩就会⽆穷的⼤，是不合理的。

//正确的⾃引⽤⽅式：
//struct Node
//{
//	int data;
//	struct Node* next;
//};

//在结构体⾃引⽤使⽤的过程中，夹杂了 typedef 对匿名结构体类型重命名，也容易引⼊问题，看看下⾯的代码，可⾏吗？
//typedef struct
//{
//	int data;
//	Node* next;
//}Node;
//这是不可行的因为typedef定义的是
//struct
//{
//	int data;
//	Node* next;
//};
//Node是对前⾯的匿名结构体类型的重命名产⽣的，在匿名结构体内部提前使⽤Node类型来创建成员变量是不⾏的。

//解决⽅案如下：定义结构体不要使⽤匿名结构体了
//typedef struct Node
//{
//	int data;
//	struct Node* next;
//}Node;
//这串代码意思就是把
//struct Node
//{
//	int data;
//	struct Node* next;
//};
//重新定义成一个新的数据类型,这个数据类型的名字叫做Node



//2. 结构体内存对⻬
//我们已经掌握了结构体的基本使⽤了。
//现在我们深⼊讨论⼀个问题：计算结构体的⼤⼩。
//这也是⼀个特别热⻔的考点： 结构体内存对⻬








